package models

import (
	"context"
	"errors"
	"go-admin/common/config"
	orm "go-admin/common/global"
	"go-admin/tools"
	"gorm.io/gorm"
	"strconv"
	"time"
)

type TUrl struct {
	ID        int `gorm:"primarykey"`
	CreatedAt *time.Time
	UpdatedAt *time.Time

	UserId int64  `json:"userId" gorm:"type:bigint;comment:UserId"`   //
	Url    string `json:"url" gorm:"type:varchar(10240);comment:Url"` //
}

type TShortUrl struct {
	ID        int64      `json:"id" gorm:"primarykey"`
	CreatedAt *time.Time `json:"createdAt"`
	UpdatedAt *time.Time `json:"updatedAt"`
	ExpiredAt *time.Time `json:"expiredAt"`
	Click     int `json:"click"`

	UserId     string `json:"userId" gorm:"type:bigint;comment:UserId"` //
	Uri        string `json:"uri"`
	Url        string `json:"url" gorm:"type:varchar(10240);comment:Url"` //
	LongUrl    string `json:"longUrl" gorm:"type:bigint;comment:LongUrl"` //
	Status     string `json:"status"`
	UserName   string `json:"username" gorm:"-"`
	Tag        string
	Type       int
	Uuid       string `json:"uuid" gorm:"-"`
	ClickLimit int
	ClickAmount int `json:"clickAmount" gorm:"-"`
}

type TShortPage struct {
	ID        int64      `json:"id" gorm:"primarykey"`
	CreatedAt *time.Time `json:"createdAt"`
	UpdatedAt *time.Time `json:"updatedAt"`
	ExpiredAt *time.Time `json:"expiredAt"`
	Click     int64      `json:"click"`

	UserId       string `json:"userId" gorm:"type:bigint;comment:UserId"` //
	Uri          string `json:"uri"`
	Url          string `json:"url" gorm:"type:varchar(10240);comment:Url"` //
	LongUrl      string `json:"longUrl" gorm:"type:bigint;comment:LongUrl"` //
	Status       string `json:"status"`
	Username     string `json:"username"`
	UserIp       string `json:"userIp"`
	UserLocation string `json:"userLocation"`
	Tag          string
}

func (TShortUrl) TableName() string {
	return "t_shorturl"
}

type TShortUrlConfig struct {
	ID       int64  `json:"id" gorm:"primarykey"`
	Url      string `json:"url" gorm:"type:varchar(10240);comment:Url"` //
	Status   int    `json:"status"`
	Priority int    `json:"priority"`
	UserTag  string `json:"userTag" gorm:"-"`
}

func (TShortUrlConfig) TableName() string {
	return "t_shorturl_config"
}

func (e *TShortUrlConfig) RandUrl() (url string) {
	orm.GetDb().Table(e.TableName()).Raw("select url from t_shorturl_config where status = 1 AND tag = ? order by" +
		" rand("+
		") limit 1", e.UserTag).First(&e)
	return e.Url
}

const seq = 4
//添加
func (e *TShortUrl) Insert() (int64, error) {
	now := time.Now()
	nextDay := now.AddDate(0, 0, 1)
	if e.UserId == "" {
		e.UserId = e.Uuid
		e.ExpiredAt = &nextDay
	}
	var err error
	//t := &TSequence{}
	//seq, err := t.NextSequence()
	//if err != nil {
	//	return 0, err
	//}
	tt := &TShortUrlConfig{}

	urlId := "/" + tools.Int2String(seq)
	e.Uri = urlId
	sysUser := &SysUser{}
	res, _ := strconv.Atoi(e.UserId)
	sysUser.UserId = res
	user, _ := sysUser.Get()
	if user.Tag == "" {
		tt.UserTag = "正经用户"
	} else {
		tt.UserTag = user.Tag
	}
	e.Url = tt.RandUrl() + urlId
	e.CreatedAt = &now
	e.UpdatedAt = &now
	e.Status = "1"

	_, err = config.GetRedis().HSet(context.Background(), urlId, "url", e.LongUrl, "status", "1").Result()
	if e.UserId == "" {
		_, err = config.GetRedis().Expire(context.Background(), urlId, time.Hour*time.Duration(24)).Result()
	}
	if err != nil {
		return 0, nil
	}
	findOne := TShortUrl{}
	err = orm.GetDb().Table(e.TableName()).Where("user_id = ? AND long_url = ? ", e.UserId,
		e.LongUrl).First(&findOne).Error
	needUpdate := true
	if err != nil {
		needUpdate = false
	}
	for true {
		if needUpdate {
			findOne.Url = e.Url
			findOne.Uri = e.Uri
			findOne.ClickLimit = e.ClickLimit
			findOne.ExpiredAt = e.ExpiredAt
			findOne.UpdatedAt = &now
			err = orm.GetDb().Table(e.TableName()).Updates(&findOne).Error
		} else {
			err = orm.GetDb().Table(e.TableName()).Create(&e).Error
		}
		if err == nil {
			break
		}
		//t := &TSequence{}
		//seq, _ = t.NextSequence()
		urlId := "/" + tools.Int2String(seq)
		e.Uri = urlId
		e.Url = tt.RandUrl() + urlId
	}
	return 0, nil
}

func (e *TShortUrl) Count() (count int64, err error) {
	if err := orm.GetDb().Table(e.TableName()).Where(e).Count(&count).Error; err != nil {
		return 0, err
	}
	return count, nil
}

func (e *TShortUrl) GetPage(pageSize int, pageIndex int) ([]TShortPage, int, error) {
	var doc []TShortPage
	table := orm.GetDb().Select("t_shorturl.id,t_shorturl.click,t_shorturl.status,t_shorturl.created_at," +
		"t_shorturl.user_id," +
		"t_shorturl.url, t_shorturl.long_url," +
		"sys_user.username, " +
		"sys_user.ip as user_ip, " +
		"sys_user.location as user_location").Table(e.TableName())
	table = table.Joins("left join sys_user on t_shorturl.user_id = sys_user.user_id")

	table = table.Where("t_shorturl.status = '1'")
	if e.UserName != "" {
		table = table.Where("sys_user.username like ?", "%"+e.UserName+"%")
	}
	//if e.Status != "" {
	//	table = table.Where("t_shorturl.status = ?", e.Status)
	//}

	if e.UserId != "" {
		table = table.Where("t_shorturl.user_id = ?", e.UserId)
	}

	if e.LongUrl != "" {
		table = table.Where("t_shorturl.long_url like ?", "%"+e.LongUrl+"%")
	}

	var count int64
	if err := table.Order("t_shorturl.updated_at desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(
		&doc).Offset(-1).
		Limit(-1).
		Count(
		&count).Error; err != nil {
		return nil, 0, err
	}
	return doc, int(count), nil
}

//修改
func (e *TShortUrl) Update(id int64) (err error) {
	var update TShortUrl
	if err = orm.GetDb().Table(e.TableName()).First(&update, id).Error; err != nil {
		return
	}

	if err = orm.GetDb().Table(e.TableName()).Model(&update).Updates(&e).Error; err != nil {
		return
	}
	return
}

func (TUrl) TableName() string {
	return "t_url"
}

func (e *TUrl) GetId() interface{} {
	return e.ID
}

type TSequence struct {
	ID uint64 `gorm:"primarykey"`

	Stub      string `json:"stub" gorm:"type:bigint;comment:Stub"` //
	Timestamp time.Time
}

func (TSequence) TableName() string {
	return "t_sequence"
}

func (e *TSequence) NextSequence() (sequence uint64, err error) {
	e.Stub = "sequence"
	//添加数据
	sql := "INSERT INTO t_sequence(stub) VALUES ('sequence') ON DUPLICATE KEY UPDATE stub = 'sequence', id = id+1"
	orm.GetDb().Table(e.TableName()).Exec(sql).Scan(&sequence)
	err = orm.GetDb().Table(e.TableName()).Select("id").First(&e).Error
	if err != nil {
		return 0, err
	}
	return e.ID, nil
}

// 获取用户数据
func (e *TUrl) Get() (url TUrl, err error) {

	table := orm.GetDb().Table(e.TableName()).Select([]string{"t_url.*"})
	if e.UserId != 0 {
		table = table.Where("user_id = ?", e.UserId)
	}

	if err = table.First(&url).Error; err != nil {
		return
	}

	return
}

func (e *TUrl) GetList() (SysUserView []SysUserView, err error) {

	table := orm.GetDb().Table(e.TableName()).Select([]string{"sys_user.*", "sys_role.role_name"})
	table = table.Joins("left join sys_role on sys_user.role_id=sys_role.role_id")
	if e.UserId != 0 {
		table = table.Where("user_id = ?", e.UserId)
	}

	if err = table.Find(&SysUserView).Error; err != nil {
		return
	}
	return
}

func (e *TUrl) GetPage(pageSize int, pageIndex int) ([]SysUserPage, int, error) {
	var doc []SysUserPage
	table := orm.GetDb().Select("sys_user.*,sys_dept.dept_name").Table(e.TableName())
	table = table.Joins("left join sys_dept on sys_dept.dept_id = sys_user.dept_id")

	// 数据权限控制(如果不需要数据权限请将此处去掉)
	dataPermission := new(DataPermission)
	table, err := dataPermission.GetDataScope(e.TableName(), table)
	if err != nil {
		return nil, 0, err
	}
	var count int64

	if err := table.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&doc).Offset(-1).Limit(-1).Count(&count).Error; err != nil {
		return nil, 0, err
	}
	return doc, int(count), nil
}

//添加
func (e *TUrl) Insert() (id int, err error) {
	// check 用户名

	//添加数据
	if err = orm.GetDb().Table(e.TableName()).Create(&e).Error; err != nil {
		return
	}
	id = e.ID
	return
}

//修改
func (e *TUrl) Update(id int) (update SysUser, err error) {
	if err = orm.GetDb().Table(e.TableName()).First(&update, id).Error; err != nil {
		return
	}
	//参数1:是要修改的数据
	//参数2:是修改的数据
	if err = orm.GetDb().Table(e.TableName()).Model(&update).Updates(&e).Error; err != nil {
		return
	}
	return
}

func (e *TUrl) BatchDelete(id []int) (Result bool, err error) {
	if err = orm.GetDb().Table(e.TableName()).Where("user_id in (?)", id).Delete(&SysUser{}).Error; err != nil {
		return
	}
	Result = true
	return
}

func (e *TUrl) GetByUserId(tx *gorm.DB, id interface{}) error {
	return tx.First(e, id).Error
}

func (e *TShortUrl) DecreaseClick() error {
	var res TShortUrl
	err := orm.GetDb().Table(e.TableName()).Where(e).First(&res).Error
	if err != nil {
		return err
	}
	// 游客状态 + 未充值状态
	if res.ClickLimit != -1 {
		if res.Click >= res.ClickLimit {
			return errors.New("余额不足")
		}
		return orm.GetDb().Exec("update t_shorturl set click = click + ? where id = ?", e.ClickAmount, res.ID).Error
	}
	var user SysUser
	notFound := SysUser{}
	err = orm.GetDb().Raw("select beishu from sys_user where user_id = ?", res.UserId).Find(&user).Error
	if err != nil {
		return err
	}
	if user == notFound {
		return errors.New("not fount user")
	}
	tx := orm.GetDb().Begin()
	e.ClickAmount = e.ClickAmount * user.Beishu
	err = tx.Exec("update t_shorturl set click = click + ? where id = ?", e.ClickAmount, res.ID).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	var rights TRights
	notFoundR := TRights{}
	sql := "select id from t_rights where user_id = ? and end_time > NOW() and remain_value >= ? order by created_at limit 1"
	err = tx.Raw(sql, res.UserId, e.ClickAmount).Find(&rights).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	// 没有流量包
	if rights == notFoundR {
		tx.Rollback()
		return errors.New("not found rights")
	}
	sql = "update t_rights set remain_value = remain_value - ? where id = ?"
	err = tx.Exec(sql, e.ClickAmount, rights.ID).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	sql = "update t_rights set consume_value = consume_value + ? where id = ?"
	err = tx.Exec(sql, e.ClickAmount, rights.ID).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}